﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.IO;
using System.Xml;
//
using Interfaces;
using MyWebpageFilter;
using MyXHTMLConverter;
using DAL;
using DTO;

namespace PreparationHost
{
    public partial class PreparationHost : UserControl, IPreparationHost
    {
        public PreparationHost()
        {
            InitializeComponent();
        }

        #region Webpage Filter
        public void FeedBack(string feedBackStr, IWebpageFilter pluginInterface)
        {
            //...
        }

        private Types.AvailableWebpageFilterPluginCollection availWFPluginList = new Types.AvailableWebpageFilterPluginCollection();
        private List<bool> activeWFPlugins = new List<bool>();

        public Types.AvailableWebpageFilterPluginCollection AvailWFPluginList
        {
            get { return availWFPluginList; }
            set { availWFPluginList = value; }
        }

        private int AddWFPlugin(string fileName)
        {
            FileInfo fi = new FileInfo(fileName);
            int countNewPlugin = 0;
            if (fi.Extension.Equals(".dll"))
            {
                try
                {
                    Assembly pluginAssembly = Assembly.LoadFrom(fileName);

                    foreach (Type pluginType in pluginAssembly.GetTypes())
                    {
                        if (pluginType.IsPublic)
                        {
                            if (!pluginType.IsAbstract)
                            {
                                Type typeInterface = pluginType.GetInterface("Interfaces.IWebpageFilter", true);

                                if (typeInterface != null)
                                {
                                    Types.AvailableWebpageFilterPlugin newPlugin = new Types.AvailableWebpageFilterPlugin();

                                    newPlugin.MyAssemblyPath = fileName;

                                    newPlugin.MyInstance = (IWebpageFilter)Activator.CreateInstance(pluginAssembly.GetType(pluginType.ToString()));

                                    newPlugin.MyInstance.PluginHost = this;

                                    //newPlugin.MyInstance.Initialize();

                                    // check if the new plugin does exist
                                    bool isExist = false;
                                    foreach (Types.AvailableWebpageFilterPlugin plugin in availWFPluginList)
                                    {
                                        if (plugin.MyInstance.PluginName.Equals(newPlugin.MyInstance.PluginName))
                                        {
                                            isExist = true;
                                        }
                                    }
                                    // add new plugin if 
                                    if (isExist == false)
                                    {
                                        this.availWFPluginList.Add(newPlugin);
                                        this.activeWFPlugins.Add(false);
                                        countNewPlugin++;
                                    }

                                    newPlugin = null;                                    
                                }

                                typeInterface = null;
                            }
                        }
                    }

                    pluginAssembly = null;
                }
                catch
                {
                    MessageBox.Show("Loading plugin dll failed!");
                }                
            }

            return countNewPlugin;
        }

        public int AddWFPlugins(string[] fileNames)
        {
            int countNewPlugin = 0;
            foreach (string file in fileNames)
            {
                countNewPlugin += AddWFPlugin(file);
            }

            if (availWFPluginList.Count == 0)
            {
                MessageBox.Show("No plugin was load!");
            }

            return countNewPlugin;
        }

        public void CloseAllWFPlugin()
        {
            foreach (Types.AvailableWebpageFilterPlugin plugin in availWFPluginList)
            {
                plugin.MyInstance.PluginDispose();

                plugin.MyInstance = null;
            }

            availWFPluginList.Clear();
        }

        public void FindWFPlugins(string path)
        {
            availWFPluginList.Clear();

            foreach (string file in Directory.GetFiles(path))
            {
                FileInfo fileInfo = new FileInfo(file);

                if (fileInfo.Extension.Equals(".dll"))
                {
                    this.AddWFPlugin(file);
                }
            }
        }

        public void FindWFPlugins()
        {
            FindWFPlugins(AppDomain.CurrentDomain.BaseDirectory);
        }

        public void LoadWFPlugins()
        {
            openFileDialog_LoadPlugin.Multiselect = true;
            openFileDialog_LoadPlugin.InitialDirectory = Directory.GetCurrentDirectory();
            openFileDialog_LoadPlugin.FileName = string.Empty;
            openFileDialog_LoadPlugin.Filter = "Plugin (*.dll) |*.dll";

            if (openFileDialog_LoadPlugin.ShowDialog() == DialogResult.OK)
            {
                if (openFileDialog_LoadPlugin.FileNames.Length != 0)
                {
                    AddWFPlugins(openFileDialog_LoadPlugin.FileNames);
                }
            }
        }
        #endregion

        #region XHTML Converter
        public void FeedBack(string feedBackStr, IXHTMLConverter pluginInterface)
        {
            //...
        }

        private Types.AvailableXHTMLConverterPluginCollection availXCPluginList = new Types.AvailableXHTMLConverterPluginCollection();

        public Types.AvailableXHTMLConverterPluginCollection AvailXCPluginList
        {
            get { return availXCPluginList; }
            set { availXCPluginList = value; }
        }

        private void AddXCPlugin(string fileName)
        {
            FileInfo fi = new FileInfo(fileName);
            if (fi.Extension.Equals(".dll"))
            {
                try
                {
                    Assembly pluginAssembly = Assembly.LoadFrom(fileName);

                    foreach (Type pluginType in pluginAssembly.GetTypes())
                    {
                        if (pluginType.IsPublic)
                        {
                            if (!pluginType.IsAbstract)
                            {
                                Type typeInterface = pluginType.GetInterface("Interfaces.IXHTMLConverter", true);

                                if (typeInterface != null)
                                {
                                    Types.AvailableXHTMLConverterPlugin newPlugin = new Types.AvailableXHTMLConverterPlugin();

                                    newPlugin.MyAssemblyPath = fileName;

                                    newPlugin.MyInstance = (IXHTMLConverter)Activator.CreateInstance(pluginAssembly.GetType(pluginType.ToString()));

                                    newPlugin.MyInstance.PluginHost = this;

                                    newPlugin.MyInstance.Initialize();

                                    this.availXCPluginList.Clear(); // Make sure there is just only one plugin at time
                                    this.availXCPluginList.Add(newPlugin);

                                    newPlugin = null;
                                }

                                typeInterface = null;
                            }
                        }
                    }

                    pluginAssembly = null;
                }
                catch
                {
                    MessageBox.Show("Loading plugin dll failed!");
                }
            }
        }

        public void AddXCPlugins(string[] fileNames)
        {
            foreach (string file in fileNames)
            {
                AddXCPlugin(file);
            }

            if (availXCPluginList.Count == 0)
            {
                MessageBox.Show("No plugin was load!");
            }
        }

        public void CloseAllXCPlugin()
        {
            foreach (Types.AvailableXHTMLConverterPlugin plugin in availXCPluginList)
            {
                plugin.MyInstance.PluginDispose();

                plugin.MyInstance = null;
            }

            availXCPluginList.Clear();
        }

        public void FindXCPlugins(string path)
        {
            availWFPluginList.Clear();

            foreach (string file in Directory.GetFiles(path))
            {
                FileInfo fileInfo = new FileInfo(file);

                if (fileInfo.Extension.Equals(".dll"))
                {
                    this.AddXCPlugin(file);
                }
            }
        }

        public void FindXCPlugins()
        {
            FindXCPlugins(AppDomain.CurrentDomain.BaseDirectory);
        }

        public void LoadXCPlugins()
        {
            openFileDialog_LoadPlugin.Multiselect = false;
            openFileDialog_LoadPlugin.InitialDirectory = Directory.GetCurrentDirectory();
            openFileDialog_LoadPlugin.FileName = string.Empty;
            openFileDialog_LoadPlugin.Filter = "Plugin (*.dll) |*.dll";

            if (openFileDialog_LoadPlugin.ShowDialog() == DialogResult.OK)
            {
                if (openFileDialog_LoadPlugin.FileName != string.Empty)
                {
                    this.availXCPluginList.Clear();                    
                    AddXCPlugin(openFileDialog_LoadPlugin.FileName);
                }
            }
        }
        #endregion

        #region Filter Webpage Plugin Functions
        /// <summary>
        /// Filter the html string with current Webpage Filter Plugins
        /// </summary>
        /// <param name="htmlStr">The html string which will be filtered</param>
        /// <returns>True if it gets through the filter, false otherwise</returns>
        public bool FilterWebpage(string htmlStr)
        {
            bool result = true;

            for (int i = 0; i < availWFPluginList.Count; i++)
            {
                if (activeWFPlugins[i] == true)
                {
                    try
                    {
                        result &= availWFPluginList.Get(i).MyInstance.IsDesirableWebpage(htmlStr);
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }
            }
            
            return result;
        }

        /// <summary>
        /// Filter files with current Webpage Filter Plugins
        /// </summary>
        /// <param name="filePaths">The file paths which will be filtered</param>
        /// <returns>List of file paths which got through filters</returns>
        public List<string> FilterWebpages(string[] filePaths)
        {
            List<string> result = new List<string>();

            FileStream fs;
            StreamReader sr;

            foreach (string file in filePaths)
            {
                if (File.Exists(file))
                {
                    fs = new FileStream(file, FileMode.Open, FileAccess.Read);
                    sr = new StreamReader(fs);
                    string htmlStr = sr.ReadToEnd();

                    if (FilterWebpage(htmlStr))
                    {
                        result.Add(file);
                    }
                }
            }

            return result;
        } 
        #endregion

        #region XHTML Converter Functions
        /// <summary>
        /// Convert a HTML file to XHTML format
        /// </summary>
        /// <param name="filePath">The HTML file path</param>
        /// <param name="newFilePath">The XHTML result file path</param>
        public void ConvertHTML2XHTML(string filePath, string newFilePath)
        {
            if (this.availXCPluginList.Count != 0)
            {
                try
                {
                    availXCPluginList.Get(0).MyInstance.ConvertToXHML(filePath, newFilePath);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            else
            {
                throw new Exception("XHTML Converter Plugin is not loaded!");
            }
        }

        /// <summary>
        /// Convert HTML files to XHTML format files
        /// </summary>
        /// <param name="filePaths">File Paths of converted files</param>
        /// <param name="newDir">The destination directory of XHTML files</param>
        /// <returns>Filepath list of new XHTML files</returns>
        public List<string> ConvertHTML2XHTML(string[] filePaths, string newDir)
        {
            List<string> result = new List<string>();

            if (this.availXCPluginList.Count != 0)
            {
                foreach (string file in filePaths)
                {
                    string[] tempString = file.Split(new string[] { @"\", @"/" }, StringSplitOptions.RemoveEmptyEntries);
                    string newFilePath = newDir + @"\\" + tempString[tempString.Length - 1];

                    try
                    {
                        ConvertHTML2XHTML(file, newFilePath);
                        result.Add(newFilePath);
                    }
                    catch
                    {
                        
                    }
                }

                return result;
            }
            else
            {
                throw new Exception("XHTML Converter Plugin is not loaded!");
            }
        } 
        #endregion

        #region Fixed Functions
        /// <summary>
        /// Insert the Local Path and URL of the web page into DB
        /// </summary>
        /// <param name="filePath">The file path which is processed</param>
        public void InsertWebpageLocalPathAndURL(string filePath)
        {
            if (File.Exists(filePath))
            {
                string[] temp = filePath.Split(new string[] { @"\", @"/" }, StringSplitOptions.RemoveEmptyEntries);
                string fileName = temp[temp.Length - 1];

                // Load the content of the webpage into string
                FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                StreamReader sr = new StreamReader(fs);
                string htmlStr = sr.ReadToEnd();

                HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument();
                htmlDoc.LoadHtml(htmlStr);

                sr.Close();
                fs.Close();

                try
                {
                    string URL = DTO.Webpage.GetOriginalUrl(htmlDoc);

                    DAL.WebpageDAL.InsertWebpage(fileName, URL);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            else
            {
                return;
            }
        }

        public void ExportToXMLPreparationResult()
        {
            XmlDocument xmlDoc = new XmlDocument();
            XmlElement xmlElementRoot = xmlDoc.CreateElement("preparation");
            xmlDoc.AppendChild(xmlElementRoot);

            XmlElement xmlElementXHTMLFolder = xmlDoc.CreateElement("xhtml_folder_path");            
            XmlText xmlText = xmlDoc.CreateTextNode(Global.xhtmlFolder);
            xmlElementXHTMLFolder.AppendChild(xmlText);

            xmlElementRoot.AppendChild(xmlElementXHTMLFolder);

            try
            {
                xmlDoc.Save(Global.xmlResultPath + @"\\" + "PreparationResult.xml");
            }
            catch
            {
                //
            }
        }
        #endregion

        #region Gui Event Handler
        private void button_LoadPlugins_Click(object sender, EventArgs e)
        {
            LoadWFPlugins();
            if (availWFPluginList.Count > 0)
            {
                this.checkedListBox_Plugins.Items.Clear();
                foreach (Types.AvailableWebpageFilterPlugin plugin in availWFPluginList)
                {
                    this.checkedListBox_Plugins.Items.Add(plugin.MyInstance.PluginName);
                }
            }
        }

        private void checkedListBox_Plugins_SelectedValueChanged(object sender, EventArgs e)
        {
            if (this.checkedListBox_Plugins.SelectedIndex >= 0)
            {
                this.richTextBox_PluginInfo.Text = this.availWFPluginList.Get(this.checkedListBox_Plugins.SelectedIndex).MyInstance.PluginDescription;
            }
        }

        private void button_HTMLFolder_Click(object sender, EventArgs e)
        {
            folderBrowserDialog_LoadConverterFolder.SelectedPath = Directory.GetCurrentDirectory();
            if (this.folderBrowserDialog_LoadConverterFolder.ShowDialog() == DialogResult.OK)
            {
                this.textBox_HTMLFolder.Text = folderBrowserDialog_LoadConverterFolder.SelectedPath;
            }
        }

        private void button_XHTMLFolder_Click(object sender, EventArgs e)
        {
            folderBrowserDialog_LoadConverterFolder.SelectedPath = Directory.GetCurrentDirectory();
            if (this.folderBrowserDialog_LoadConverterFolder.ShowDialog() == DialogResult.OK)
            {
                this.textBox_XHTMLFolder.Text = folderBrowserDialog_LoadConverterFolder.SelectedPath;
            }
        }

        private void button_ConverterPlugin_Click(object sender, EventArgs e)
        {
            LoadXCPlugins();

            if (availXCPluginList.Count != 0)
            {
                this.textBox_ConverterPlugin.Text = availXCPluginList.Get(0).MyInstance.PluginName;
            }
        }

        private void button_Set_Click(object sender, EventArgs e)
        {
            if (this.textBox_HTMLFolder.Text != string.Empty)
            {
                Global.htmlFolder = this.textBox_HTMLFolder.Text;
            }

            if (this.textBox_XHTMLFolder.Text != string.Empty)
            {
                Global.xhtmlFolder = this.textBox_XHTMLFolder.Text;
            }

            if (Directory.Exists(Global.htmlFolder))
            {
                string[] files = Directory.GetFiles(Global.htmlFolder);

                listView_HTMLFiles.Items.Clear();
                foreach (string file in files)
                {
                    FileInfo fi = new FileInfo(file);
                    if (fi.Extension.Equals(".html") || fi.Extension.Equals(".htm"))
                    {
                        string[] temp = file.Split(new string[] { @"/", @"\" }, StringSplitOptions.RemoveEmptyEntries);
                        listView_HTMLFiles.Items.Add(temp[temp.Length - 1]);
                        Global.htmlFiles.Add(file);
                    }
                }
            }

            groupBox_Converter.Enabled = false;
        }

        private void button_ClearPlugins_Click(object sender, EventArgs e)
        {
            this.checkedListBox_Plugins.Items.Clear();
            this.availWFPluginList.Clear();
            this.activeWFPlugins.Clear();
        }

        private void button_PreparationRun_Click(object sender, EventArgs e)
        {
            this.button_PreparationRun.Enabled = false;
            try
            {
                // Convert to XHTML
                ConvertHTML2XHTML(Global.htmlFiles.ToArray<string>(), Global.xhtmlFolder);
                // Filter          
                List<string> xhtmlFiles = Directory.GetFiles(Global.xhtmlFolder).ToList<string>();
                List<string> filteredXhtmlFiles = FilterWebpages(xhtmlFiles.ToArray<string>());

                foreach (string file in xhtmlFiles)
                {
                    if (filteredXhtmlFiles.Contains(file) == false)
                    {
                        File.Delete(file);
                    }
                }

                listView_XHTMLFiles.Items.Clear();
                foreach (string file in filteredXhtmlFiles)
                {
                    string[] temp = file.Split(new string[] { @"/", @"\" }, StringSplitOptions.RemoveEmptyEntries);
                    listView_XHTMLFiles.Items.Add(temp[temp.Length - 1]);
                    Global.xhtmlFiles.Add(file);

                    this.InsertWebpageLocalPathAndURL(file);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            this.button_PreparationRun.Enabled = true;
        }

        private void button_Export2XML_Click(object sender, EventArgs e)
        {
            folderBrowserDialog_LoadConverterFolder.SelectedPath = Directory.GetCurrentDirectory();
            if (folderBrowserDialog_LoadConverterFolder.ShowDialog() == DialogResult.OK)
            {
                Global.xmlResultPath = folderBrowserDialog_LoadConverterFolder.SelectedPath;
            }

            ExportToXMLPreparationResult();

            MessageBox.Show("Export complete!");
        }

        private void checkedListBox_Plugins_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            int i = this.checkedListBox_Plugins.SelectedIndex;
            this.activeWFPlugins[i] = !checkedListBox_Plugins.GetItemChecked(i);
            if (activeWFPlugins[i] == true)
            {
                availWFPluginList.Get(i).MyInstance.Initialize();
            }
        }

        public void HideAllControl()
        {
            groupBox_Converter.Enabled = false;
            groupBox_FilterSetting.Enabled = false;
            groupBox_PreparationView.Enabled = false;
        } 
        #endregion

        private void checkedListBox_Plugins_SelectedIndexChanged(object sender, EventArgs e)
        {

        }
    }

    namespace Types
    {
        #region Webpage Filter Types
        public class AvailableWebpageFilterPlugin
        {
            private IWebpageFilter myInstance = null;

            public IWebpageFilter MyInstance
            {
                get { return myInstance; }
                set { myInstance = value; }
            }

            private string myAssemblyPath = string.Empty;

            public string MyAssemblyPath
            {
                get { return myAssemblyPath; }
                set { myAssemblyPath = value; }
            }

        }

        public class AvailableWebpageFilterPluginCollection : System.Collections.CollectionBase
        {
            public void Add(Types.AvailableWebpageFilterPlugin plugin)
            {
                this.List.Add(plugin);
            }

            public void Remove(Types.AvailableWebpageFilterPlugin plugin)
            {
                this.List.Remove(plugin);
            }

            public AvailableWebpageFilterPlugin Get(int idx)
            {
                try
                {
                    return (AvailableWebpageFilterPlugin)this.List[idx];
                }
                catch (IndexOutOfRangeException indexOutOfRangeEx)
                {
                    throw indexOutOfRangeEx;
                }
            }

            public AvailableWebpageFilterPlugin Find(string pluginNameOrPath)
            {
                AvailableWebpageFilterPlugin result = null;
                foreach (AvailableWebpageFilterPlugin plugin in this.List)
                {
                    if ((plugin.MyInstance.PluginName.Equals(pluginNameOrPath))
                        || (plugin.MyAssemblyPath.Equals(pluginNameOrPath)))
                    {
                        result = plugin;
                        break;
                    }
                }

                return result;
            }
        } 
        #endregion

        #region XHTML Converter Types
        public class AvailableXHTMLConverterPlugin
        {
            private IXHTMLConverter myInstance = null;

            public IXHTMLConverter MyInstance
            {
                get { return myInstance; }
                set { myInstance = value; }
            }

            private string myAssemblyPath = string.Empty;

            public string MyAssemblyPath
            {
                get { return myAssemblyPath; }
                set { myAssemblyPath = value; }
            }

        }

        public class AvailableXHTMLConverterPluginCollection : System.Collections.CollectionBase
        {
            public void Add(Types.AvailableXHTMLConverterPlugin plugin)
            {
                this.List.Add(plugin);
            }

            public void Remove(Types.AvailableXHTMLConverterPlugin plugin)
            {
                this.List.Remove(plugin);
            }

            public AvailableXHTMLConverterPlugin Get(int idx)
            {
                try
                {
                    return (AvailableXHTMLConverterPlugin)this.List[idx];
                }
                catch (IndexOutOfRangeException indexOutOfRangeEx)
                {
                    throw indexOutOfRangeEx;
                }
            }

            public AvailableXHTMLConverterPlugin Find(string pluginNameOrPath)
            {
                AvailableXHTMLConverterPlugin result = null;
                foreach (AvailableXHTMLConverterPlugin plugin in this.List)
                {
                    if ((plugin.MyInstance.PluginName.Equals(pluginNameOrPath))
                        || (plugin.MyAssemblyPath.Equals(pluginNameOrPath)))
                    {
                        result = plugin;
                        break;
                    }
                }

                return result;
            }
        } 
        #endregion
    }
}
